// $codepro.audit.disable overloadedMethods
/*
 * ClientManager.java
 */
package edu.gatech.oad.networking;

import java.awt.BorderLayout;
import java.io.IOException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
import javax.swing.JFrame;
import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import edu.gatech.oad.ui.MainPanel;
import edu.gatech.oad.user.AccountsController;

public class ClientManager implements ServiceListener, ListModel {

	private static final ClientManager SHARED_MANAGER = new ClientManager();
	
	private IServiceProvider serviceProvider = null;
	
	private JmDNS mDNS = null;
	
	private Registry registry = null;
	
	private ServerDiscoveryFrame discoveryFrame = null;
	
	private final List<ServiceInfo> discoveredServices = new ArrayList<ServiceInfo>();
	
	private final Set<ListDataListener> listeners = new HashSet<ListDataListener>();
	
	private ClientManager() { // $codepro.audit.disable emptyMethod
		
	}
	
	public static ClientManager sharedManager() {
		return SHARED_MANAGER;
	}
	
	public List<ServiceInfo> getDiscoveredServices() {
		return new ArrayList<ServiceInfo>(this.discoveredServices);
	}
	
	public void beginDiscovery() {
		discoveryFrame = new ServerDiscoveryFrame();
		discoveryFrame.setVisible(true);
		discoveryFrame.beginDiscovery();
	}
	
	public void setupNetworking() throws IOException {
		this.mDNS = JmDNS.create();
		this.mDNS.addServiceListener("_mufasaserver._tcp.local.", this);
	}
	
	public void connectToServer(int index) {
		connectToServer(this.discoveredServices.get(index));
	}
	
	public void connectToServer(ServiceInfo info) {
		assert (info != null);
		if (serviceProvider != null) {
			this.disconnectFromServer();
		}
		try {
			this.registry = LocateRegistry.getRegistry(
					info.getHostAddresses()[0],
					info.getPort());
			this.serviceProvider = (IServiceProvider) this.registry.lookup(
					"IServiceProvider");
			
			final JFrame frame = new JFrame(); //The main frame
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
			frame.setLayout(new BorderLayout());
	        frame.setTitle("M.U.F.A.S.A. (Simba Build)");
	        JFrame.setDefaultLookAndFeelDecorated(true);  
	        final MainPanel mainPanel = new MainPanel(frame); //makes sure the main panel can access the frame
	        frame.add(mainPanel, BorderLayout.CENTER);
	        frame.setResizable(true);
	        
	        AccountsController.setSharedController(
	        		this.serviceProvider.getCurrentAccounts());
	        
	        frame.getContentPane(); // $codepro.audit.disable com.instantiations.assist.eclipse.analysis.unusedReturnValue
	        frame.pack();
			frame.setVisible(true);
			
			this.discoveryFrame.setVisible(false);
			this.discoveryFrame = null;
		}
		catch (Exception e) {
			//Unfortunately, can't use a more fine-grained exception class since this is the only common superclass of RemoteException and NotBoundException
			this.registry = null;
			this.serviceProvider = null;
			e.printStackTrace();
		}
	}
	
	public void disconnectFromServer() {
		this.serviceProvider = null;
	}

	//ServiceListener interface methods
	@Override
	public void serviceAdded(ServiceEvent arg0) {
		System.out.println("Service added");
	}

	@Override
	public void serviceRemoved(ServiceEvent arg0) {
		// TODO Need to handle LDLs
		System.out.println("Service removed");
		this.discoveredServices.remove(arg0.getInfo());
		for (ListDataListener listener : this.listeners) {
			listener.intervalAdded(new ListDataEvent(
					this, ListDataEvent.INTERVAL_ADDED, 
					this.discoveredServices.size(), 
					this.discoveredServices.size()));
		}
	}

	@Override
	public void serviceResolved(ServiceEvent arg0) {
		// TODO Need to handle LDLs
		System.out.println("Service resolved");
		final ServiceInfo info = arg0.getInfo();
		this.discoveredServices.add(info);
		for (ListDataListener listener : this.listeners) {
			listener.intervalAdded(new ListDataEvent(
					this, ListDataEvent.INTERVAL_ADDED,
					this.discoveredServices.size() - 1,
					this.discoveredServices.size() - 1));
		}
	}

	//ListModel methods
	@Override
	public void addListDataListener(ListDataListener listener) {
		// TODO Auto-generated method stub
		this.listeners.add(listener);
	}

	@Override
	public Object getElementAt(int index) {
		// TODO Auto-generated method stub
		final ServiceInfo info = this.discoveredServices.get(index);
		
		return info.getName() + " on " + info.getServer();
	}

	@Override
	public int getSize() {
		// TODO Auto-generated method stub
		return this.discoveredServices.size();
	}

	@Override
	public void removeListDataListener(ListDataListener listener) {
		// TODO Auto-generated method stub
		this.listeners.remove(listener);
	}
	
	@Override
	public String toString() {
		return super.toString() + " " + this.discoveredServices + " " + this.listeners;
	}
}
